package ar.uba.fi.tecnicas.fabrica;

import ar.uba.fi.tecnicas.fabrica.exceptions.PosicionTerrenoOcupadaException;
import ar.uba.fi.tecnicas.fabrica.exceptions.TransformacionInvalidaException;
import ar.uba.fi.tecnicas.juego.GeneradorAleatorio;
import ar.uba.fi.tecnicas.juego.Tiempo;
import ar.uba.fi.tecnicas.juego.exceptions.TipoMaquinaFaltanteException;

public class Maquina extends ItemLineaProduccion implements IConectable
{
	protected TipoMaquina tipo = null;
	
	protected double estado = 100.0;
	
	protected long millisInicioProceso;
	
	public Maquina(TipoMaquina tipo, PosicionTerreno posicion) 
	throws 
		PosicionTerrenoOcupadaException,
		TipoMaquinaFaltanteException
	{
		if(tipo == null)
		{
			throw new TipoMaquinaFaltanteException();
		}

		this.tipo = tipo;		

		this.setPosicion(posicion);
	}
	
	public TipoMaquina getTipo()
	{
		return this.tipo;
	}
	
	public void setPosicion(PosicionTerreno posicion)
	{
		this.posicion = posicion;
		
		posicion.setItem(this);
		
		this.addObserver(posicion);
	}

	// Devuelve la pieza si es que tiene una y si
	// esta termino de procesarse
	public Pieza entregar()
	{
		Pieza pieza = null;

		if (this.pieza != null && this.procesoTerminado())
		{
			pieza = this.pieza;
			this.pieza = null;
			// Desasociar pieza de item
			pieza.entregar();
			
			this.millisInicioProceso = -1;
		}
		
		return pieza;
	}

	// Indica si el proceso que debe realizar 
	// la maquina finalizo
	private boolean procesoTerminado() 
	{
		boolean terminado = false;

		long millisTranscurridos = Tiempo.instancia.transcurrido() - this.millisInicioProceso; 
		
		if(millisTranscurridos >= this.tipo.millisPorPieza())
		{
			terminado = true;
		}
		
		return terminado;
	}

	// Aplica la transformacion 
	protected void procesar()
	{
		this.averia();

		this.millisInicioProceso = Tiempo.instancia.transcurrido();

		try
		{
			this.tipo.getTransformacion().transformar(pieza, this.estado);
		}
		catch(TransformacionInvalidaException ex) {}
	}

	protected boolean averia()
	{
		boolean averia = false;
		
		if(GeneradorAleatorio.generar() < this.tipo.getAveriaFrecuencia())
		{
			double delta = (GeneradorAleatorio.generar() / 100) * this.tipo.getAveriaMaxima();
			
			this.estado -= delta;
			
			if(this.estado < 0)
			{
				this.estado = 0;
			}

			averia = true;
			
			this.setChanged();
			this.notifyObservers();
		}

		return averia;
	}

	public void setEntrada(IConector entrada)
	{		
		this.entrada = (ItemLineaProduccion)entrada;
		this.setChanged();
	}

	public void setSalida(IConector salida) 
	{
		this.salida = (ItemLineaProduccion)salida;
		this.setChanged();
	}
	
	public void quitarEntrada(IConector entrada) 
	{
		this.entrada = null;
		this.setChanged();
	}

	public void quitarSalida(IConector salida) 
	{	
		this.salida = null;
		this.setChanged();
	}
	
	protected void validarIngresar(Pieza pieza) throws NullPointerException 
	{
		if(pieza == null)
			throw new NullPointerException();
	}
	
	public double getEstado() 
	{ 
		return estado; 
	}
	
	public ItemLineaProduccionInfo getInfo()
	{
		return new MaquinaInfo(this);
	}

	public boolean fueraDeServicio() 
	{
		return this.estado == 0;
	}

	public void averiar() 
	{
		this.estado = 0;		
	}
	
	public void arreglar(double porcentajeArreglo) 
	{
		if(this.estado + porcentajeArreglo <= 100)
		{
			this.estado += porcentajeArreglo;	
		}
		else
		{
			this.estado = 100;
		}
		
		this.setChanged();
		this.notifyObservers();
	}
	
	public class MaquinaInfo extends ItemLineaProduccionInfo
	{
		private Maquina maquina; 
		public MaquinaInfo(ItemLineaProduccion item)
		{
			super(item);
			this.maquina = (Maquina)item;
		}
		
		public String getNombre()
		{
			return this.maquina.getTipo().getNombre();
		}
		
		public String getDescripcion()
		{
			return null;
		}
		
		public String getEstado()
		{
			return String.valueOf((int)this.maquina.estado) + "%";
		}		
	}

	public void eliminar()
	{		
		if(this.entrada != null)
		{
			((IConector)this.entrada).desconectar();
		}
		
		if(this.salida != null)
		{		
			((IConector)this.salida).desconectar();
		}
		
		if(this.lineaProduccion != null)
		{		
			this.lineaProduccion.quitarItem(this);
		}
		
		this.setChanged();
		this.notifyObservers();
		this.deleteObservers();
	}
}
